/*
Copyright 2010 Thomas W. Holtquist

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Touch;

using GHIElectronics.NETMF.Hardware;

namespace Skewworks.Pyxis.Kernel
{

    public static class SettingsManager
    {

        #region Constructor

        static SettingsManager()
        {
            LoadBootSettings();
            LoadLCDSettings();
            LoadUpdateSettings();
        }

        #endregion

        #region LCD EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SavedLCDSettings;

        private static Pyxis2LCDSettings _lcd;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2LCDSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2LCDSettings(ScreenCalibration iCalibrate, int iCalibrationPoints, short[] icalibrationSX, short[] icalibrationSY, short[] icalibrationCX, short[] icalibrationCY)
            {
                calibrateLCD = iCalibrate;
                calibrationPoints = iCalibrationPoints;
                calibrationSX = icalibrationSX;
                calibrationSY = icalibrationSY;
                calibrationCX = icalibrationCX;
                calibrationCY = icalibrationCY;
            }

            // Fields must be serializable.
            public ScreenCalibration calibrateLCD;
            public int calibrationPoints;
            public short[] calibrationSX;
            public short[] calibrationSY;
            public short[] calibrationCX;
            public short[] calibrationCY;
        }

        #endregion

        #region Boot EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedBootSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SavedBootSettings;

        private static Pyxis2BootSettings _boot;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2BootSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2BootSettings(NetworkConnectionType ConnectionType, string IPAddress, string GatewayAddress, string SubnetMask, bool CheckUpdates, bool EnableRTC, byte Volume, byte TrebleAmp, byte TrebleLimit, byte BassAmp, byte BassLimit)
            {
                this.ConnectionType = ConnectionType;
                this.IPAddress = IPAddress;
                this.GatewayAddress = GatewayAddress;
                this.SubnetMask = SubnetMask;
                this.CheckUpdates = CheckUpdates;
                this.EnableRTC = EnableRTC;
                this.Volume = Volume;
                this.TrebleAmp = TrebleAmp;
                this.TrebleLimit = TrebleLimit;
                this.BassAmp = BassAmp;
                this.BassLimit = BassLimit;
            }

            // Fields must be serializable.
            public NetworkConnectionType ConnectionType;
            public string IPAddress;
            public string GatewayAddress;
            public string SubnetMask;
            public bool CheckUpdates;
            public bool EnableRTC;
            public byte Volume;
            public byte TrebleAmp;
            public byte TrebleLimit;
            public byte BassAmp;
            public byte BassLimit;
        }

        #endregion

        #region Update EWR

        /// This private class is used as a signature to uniquely identify data 
        /// we own.  It is not necessary to create a dedicated class for this 
        /// purpose.
        /// </summary>
        private class Pyxis2SavedSysUpdateSettings { }

        /// <summary>
        /// Saved user settings
        /// </summary>
        private static ExtendedWeakReference SystemUpdateSettings;

        private static Pyxis2SysUpdateSettings _update;

        /// <summary>
        /// This is the class that will be stored in Flash memory.  This class 
        /// must be serializable.
        /// </summary>
        [Serializable]
        public sealed class Pyxis2SysUpdateSettings
        {
            /// <summary>
            /// This method sets the number of boots.
            /// </summary>
            /// <param name="bootCount">The number of times the device has been 
            /// booted.</param>
            public Pyxis2SysUpdateSettings(bool RequireUpdate, string FileLocation, int iResult)
            {
                this.NeedsUpdate = RequireUpdate;
                this.FilePath = FileLocation;
                this.LastResult = iResult;
            }

            // Fields must be serializable.
            public bool NeedsUpdate;
            public string FilePath;
            public int LastResult;
        }

        #endregion

        #region LCD Resolution

        internal static void screen320x240()
        {
            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();

            lcdConfig.Width = 320;
            lcdConfig.Height = 240;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = false;
            lcdConfig.OutputEnableIsFixed = true;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.PixelPolarity = true;
            lcdConfig.HorizontalSyncPulseWidth = 41;
            lcdConfig.HorizontalBackPorch = 27;
            lcdConfig.HorizontalFrontPorch = 51;
            lcdConfig.VerticalSyncPulseWidth = 10;
            lcdConfig.VerticalBackPorch = 8;
            lcdConfig.VerticalFrontPorch = 16;
            lcdConfig.PixelClockDivider = 8;

            // Set config
            Configuration.LCD.Set(lcdConfig);
        }

        internal static void screen480x272()
        {
            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();
            lcdConfig.Width = 480;
            lcdConfig.Height = 272;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = false;
            lcdConfig.OutputEnableIsFixed = true;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.PixelPolarity = false;
            lcdConfig.HorizontalSyncPulseWidth = 41;
            lcdConfig.HorizontalBackPorch = 2;
            lcdConfig.HorizontalFrontPorch = 2;
            lcdConfig.VerticalSyncPulseWidth = 10;
            lcdConfig.VerticalBackPorch = 2;
            lcdConfig.VerticalFrontPorch = 2;

            // NOTE: This is used for EMX, comment if using ChipworkX.
            lcdConfig.PixelClockDivider = 8;

            // Set configs
            Configuration.LCD.Set(lcdConfig);
        }

        internal static void screen800x480()
        {
            if (SystemInfo.SystemID.SKU == 3)
            {
                Debug.Print("Skip setting screen resolution (Inside Emulator)");
                return;
            }

            Configuration.LCD.Configurations lcdConfig = new Configuration.LCD.Configurations();
            lcdConfig.Width = 800;
            lcdConfig.Height = 480;

            // Only use if needed, see documentation.
            lcdConfig.PriorityEnable = true;
            lcdConfig.OutputEnableIsFixed = false;
            lcdConfig.OutputEnablePolarity = true;
            lcdConfig.PixelPolarity = false;
            lcdConfig.HorizontalSyncPolarity = false;
            lcdConfig.VerticalSyncPolarity = false;
            lcdConfig.HorizontalSyncPulseWidth = 150;
            lcdConfig.HorizontalBackPorch = 150;
            lcdConfig.HorizontalFrontPorch = 150;
            lcdConfig.VerticalSyncPulseWidth = 2;
            lcdConfig.VerticalBackPorch = 2;
            lcdConfig.VerticalFrontPorch = 2;
            lcdConfig.PixelClockDivider = 4;

            // Set config
            Configuration.LCD.Set(lcdConfig);
        }

        #endregion

        #region Properties

        public static Pyxis2BootSettings BootSettings
        {
            get { return _boot; }
        }

        public static Pyxis2LCDSettings LCDSettings
        {
            get { return _lcd; }
        }

        public static Pyxis2SysUpdateSettings SystemUpdate
        {
            get { return _update; }
        }

        #endregion

        #region Public Methods

        public static bool SaveLCDCalibration(int iCalibrationPoints, short[] icalibrationSX, short[] icalibrationSY, short[] icalibrationCX, short[] icalibrationCY)
        {
            // Only allow default domain
            if (AppDomain.CurrentDomain.FriendlyName != "default") return false;

            // Don't set logo in emulator
            if (SystemInfo.SystemID.SKU == 3) return false;
            
            // Save calibration
            _lcd.calibrateLCD = ScreenCalibration.Restore;
            _lcd.calibrationPoints = iCalibrationPoints;
            _lcd.calibrationSX = icalibrationSX;
            _lcd.calibrationSY = icalibrationSY;
            _lcd.calibrationCX = icalibrationCX;
            _lcd.calibrationCY = icalibrationCY;
            SavedLCDSettings.Target = _lcd;

            // Set calibration
            Touch.ActiveTouchPanel.SetCalibration(_lcd.calibrationPoints, _lcd.calibrationSX, _lcd.calibrationSY, _lcd.calibrationCX, _lcd.calibrationCY);

            return true;
        }

        public static bool RestoreLCDCalibration()
        {
            // Restore calibration data if we have it.
            try
            {
                if (_lcd != null)
                {
                    if (_lcd.calibrateLCD == ScreenCalibration.Restore)
                    {
                        Touch.ActiveTouchPanel.SetCalibration(_lcd.calibrationPoints, _lcd.calibrationSX, _lcd.calibrationSY, _lcd.calibrationCX, _lcd.calibrationCY);
                        return true;
                    }
                }
            }
            catch (Exception) { }

            return false;
        }

        #endregion

        #region Internal Methods

        internal static void RequireLCDCalibration()
        {
            _lcd = new Pyxis2LCDSettings(ScreenCalibration.Gather, 0, null, null, null, null);
            SavedLCDSettings.Target = _lcd;
        }

        internal static void SaveBootSettings(NetworkConnectionType ConnectionType, string IPAddress, string GatewayAddress, string SubnetMask, bool CheckUpdates, bool EnableRTC, byte Volume, byte TrebleAmp, byte TrebleLimit, byte BassAmp, byte BassLimit)
        {
            _boot = new Pyxis2BootSettings(ConnectionType, IPAddress, GatewayAddress, SubnetMask, CheckUpdates, EnableRTC, Volume, TrebleAmp, TrebleLimit, BassAmp, BassLimit);
            SavedBootSettings.Target = _boot;
        }

        #endregion

        #region Private Methods

        private static void LoadBootSettings()
        {
            // Grab the data
            SavedBootSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedBootSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SavedBootSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _boot = (Pyxis2BootSettings)SavedBootSettings.Target;

            if (_boot == null)
            {
                _boot = new Pyxis2BootSettings(NetworkConnectionType.DHCP, "", "", "", true, false, 100, 0, 0, 0, 0);
                SavedBootSettings.Target = _boot;
            }

        }

        private static void LoadLCDSettings()
        {
            // Grab the data
            SavedLCDSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SavedLCDSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _lcd = (Pyxis2LCDSettings)SavedLCDSettings.Target;
        }

        private static void LoadUpdateSettings()
        {
            // Grab the data
            SystemUpdateSettings = ExtendedWeakReference.RecoverOrCreate(typeof(Pyxis2SavedSysUpdateSettings), 0, ExtendedWeakReference.c_SurvivePowerdown);
            SystemUpdateSettings.Priority = (Int32)ExtendedWeakReference.PriorityLevel.Critical;
            _update = (Pyxis2SysUpdateSettings)SystemUpdateSettings.Target;
        }

        #endregion

    }

}
